package poc.bcsoi.utils;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.GregorianCalendar;

import javax.xml.namespace.NamespaceContext;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import poc.bcsoi.utils.BcsoiXpath.Expressions;
import poc.spring.model.BcsoiCacheQueryParams;
import poc.spring.xml.DomParser;

public class BcsoiQueryParamUtils {
	
//	private Document doc;
//	private String xmlSrc;
	
	public BcsoiQueryParamUtils() {	}
	
	public static BcsoiCacheQueryParams createBcsoiQueryParam(String xmlSrc, NamespaceContext context) {
		BcsoiCacheQueryParams bcqp = new BcsoiCacheQueryParams();		

		DomParser parser = new DomParser();		
		Document doc = parser.parseXmlFile(xmlSrc, context);

		bcqp.setAdapterName(getNode(doc, context, BcsoiXpath.Expressions.ADAPTER_NAME.toString()).get(0));
		bcqp.setIdentityQualifier(getNode(doc, context, BcsoiXpath.Expressions.IDENTITY_QUALIFIER.toString()).get(0));
		bcqp.setIdentityValue(getNode(doc, context, BcsoiXpath.Expressions.IDENTITY_VALUE.toString()).get(0));
		bcqp.setCategory(getNode(doc, context, Expressions.CATEGORY.toString()).get(0));
		bcqp.setExpirationDTG(DateUtils.convertStringToGregorianCalendar(getNode(doc, context, BcsoiXpath.Expressions.EXPIRATION_DATE.toString()).get(0)));		
		bcqp.setCreatedtg(DateUtils.convertStringToGregorianCalendar(getNode(doc, context, BcsoiXpath.Expressions.CREATED_DATE.toString()).get(0)));
		bcqp.setUpdatedtg(DateUtils.convertStringToGregorianCalendar(getNode(doc, context, BcsoiXpath.Expressions.UPDATED_DATE.toString()).get(0)));
		bcqp.setJson(getNode(doc, context, BcsoiXpath.Expressions.JSON.toString()).get(0));
		bcqp.setNorth(Double.parseDouble(getNode(doc, context, BcsoiXpath.Expressions.NORTH.toString()).get(0)));
		bcqp.setEast (Double.parseDouble(getNode(doc, context, BcsoiXpath.Expressions.EAST.toString()).get(0)));
		bcqp.setWest (Double.parseDouble(getNode(doc, context, BcsoiXpath.Expressions.WEST.toString()).get(0)));
		bcqp.setSouth(Double.parseDouble(getNode(doc, context, BcsoiXpath.Expressions.SOUTH.toString()).get(0)));
		bcqp.setKml (getNode(doc, context, BcsoiXpath.Expressions.KML.toString()).get(0));
		bcqp.setDdmsResource(getNode(doc, context, BcsoiXpath.Expressions.DDMS_RESOURCE.toString()).get(0));

		// has to be done last becuase it's changing the contents of the document
		bcqp.setResourceXml (retrieveResourceXmlData(doc, context));
				
		return bcqp;
	}

	public static String retrieveIdentityQualifier(String xmlSrc, NamespaceContext context) {
		DomParser parser = new DomParser();		
		System.out.println("IdentityQualifier Xpath expression: " + BcsoiXpath.Expressions.IDENTITY_QUALIFIER.toString());
		String result = parser.parseXmlFile(xmlSrc, context, BcsoiXpath.Expressions.IDENTITY_QUALIFIER.toString()).get(0);
		System.out.println("IdentityQualifier: " + result);
		
		return result;
	}	
	
	public static String retrieveIdentityValue(String xmlSrc, NamespaceContext context) {
		DomParser parser = new DomParser();		
		System.out.println("IdentityValue Xpath Expression: " + BcsoiXpath.Expressions.IDENTITY_VALUE.toString());
		String result = parser.parseXmlFile(xmlSrc, context, BcsoiXpath.Expressions.IDENTITY_VALUE.toString()).get(0);
		System.out.println("Result: " + result);
		
		return result;
	}
	
	public static GregorianCalendar retrieveCreatedDate(String xmlSrc, NamespaceContext context) {
		System.out.println("CreatedDtg Xpath expression: " + BcsoiXpath.Expressions.CREATED_DATE.toString());
		
		String createdDate = retrieveXmlNode(xmlSrc, BcsoiXpath.Expressions.CREATED_DATE.toString(), context);
		GregorianCalendar createdDtg = DateUtils.convertStringToGregorianCalendar(createdDate);
		
		System.out.println("Created Date: " + createdDtg);
		
		return createdDtg;
	}
	
	
	public static String retrieveXmlNode(String xmlSrc, String xpathExpr, NamespaceContext context) {
		DomParser parser = new DomParser();		
		System.out.println("Xpath expression: " + xpathExpr);
		String result = parser.parseXmlFile(xmlSrc, context, xpathExpr).get(0);
		System.out.println("Result: " + result);
		
		return result;
	}
	
	
	public static String retrieveDdmsResource(String xmlSrc, NamespaceContext context) {
		DomParser parser = new DomParser();		
		System.out.println("DDMS Resouce Xpath Expression: " + BcsoiXpath.Expressions.DDMS_RESOURCE.toString());
		String result = parser.parseXmlFile(xmlSrc, context, BcsoiXpath.Expressions.DDMS_RESOURCE.toString()).get(0);
		System.out.println("Result: " + result);
		
		return result;
	}
	

	public static String retrieveResourceXmlData(String xmlSrc, NamespaceContext context) {
		String ddmsResource = "";
		DomParser parser = new DomParser();
		Document doc = parser.parseXmlFile(xmlSrc, context);
		printNode(doc);
		
		// Get appropriate node
		Element element = (Element) doc.getElementsByTagName("bcsoi:Item").item(0);
		// remove element from parent node
		element.getParentNode().removeChild(element);
		// Normalize the DOM tree to combine all adjacent text nodes
		doc.normalize();
		
		printNode(doc);
		
		ddmsResource = parser.xmlFragmentAsString(doc);
		
		return ddmsResource;
	}
	
	
	public static String retrieveResourceXmlData(Document doc, NamespaceContext context) {
		String ddmsResource = "";
		printNode(doc);
		
		// Get appropriate node
		Element element = (Element) doc.getElementsByTagName("bcsoi:Item").item(0);
		// remove element from parent node
		element.getParentNode().removeChild(element);
		// Normalize the DOM tree to combine all adjacent text nodes
		doc.normalize();
		
		printNode(doc);
		
		ddmsResource = xmlFragmentAsString(doc);
		
		return ddmsResource;
	}
	
	
	public static void printNode(Document node) {
		String str = "";
		try {
			TransformerFactory transFactory = TransformerFactory.newInstance();
			StringWriter buffer = new StringWriter();			
			
			Transformer transformer = transFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			transformer.transform(new DOMSource(node), new StreamResult(buffer));

			str = buffer.toString().trim();
			
		} catch (TransformerConfigurationException tce) {
			tce.printStackTrace();
		} catch (TransformerException tfe) {
			tfe.printStackTrace();
		}

		System.out.println(str);
	}
	
	
	public static String xmlFragmentAsString(Node node) {
		String str = "";
		try {
			TransformerFactory transFactory = TransformerFactory.newInstance();
			StringWriter buffer = new StringWriter();			
			
			Transformer transformer = transFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			transformer.transform(new DOMSource(node), new StreamResult(buffer));

			str = buffer.toString().trim();
			
		} catch (TransformerConfigurationException tce) {
			tce.printStackTrace();
		} catch (TransformerException tfe) {
			tfe.printStackTrace();
		}

		return str;
	}
	
	
	public static ArrayList<String> getNode(Document doc, NamespaceContext context, String xpathExpression) {
		ArrayList<String> fragmentNodes = new ArrayList<String>();
		
		try {			
			
			XPathFactory xFactory = XPathFactory.newInstance();
			XPath xpath = xFactory.newXPath();
			xpath.setNamespaceContext(context);
			XPathExpression expr = xpath.compile(xpathExpression);
			Object result = expr.evaluate(doc, XPathConstants.NODESET);
			  
			NodeList nodes = (NodeList) result;			
			for (int i = 0; i < nodes.getLength(); i++) {
				System.out.println("nodes length: " + nodes.getLength());
				
				NodeList childNodes = nodes.item(i).getChildNodes();
				System.out.println("childNodes length: " + childNodes.getLength());
				
				for (int j = 0; j < childNodes.getLength(); j++) {
					String currentNode = xmlFragmentAsString(childNodes.item(j)); 
					System.out.println( currentNode );
					if ( currentNode.length() > 0 ) {
						fragmentNodes.add(currentNode);
					}
				} 
			  }
			
			for (String xmlFrag : fragmentNodes) {
				System.out.println("Node: \n" + xmlFrag);
			}
			
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}		
		
		if ( fragmentNodes.size() <= 0 ) {
			fragmentNodes.add(null);
		}

		return fragmentNodes;
	}
	
}
